home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / lisp / clue.lha / clue / gc-cache.l < prev    next >
Lisp/Scheme  |  1989-07-12  |  13KB  |  317 lines

  1. ;;; -*- Mode:Lisp; Package:XLIB; Syntax:COMMON-LISP; Base:10; Lowercase:T -*-
  2.  
  3. ;;; CLX Using-Gcontext macro
  4.  
  5. ;;;
  6. ;;;             TEXAS INSTRUMENTS INCORPORATED
  7. ;;;                  P.O. BOX 2909
  8. ;;;                   AUSTIN, TEXAS 78769
  9. ;;;
  10. ;;; Copyright (C) 1987 Texas Instruments Incorporated.
  11. ;;;
  12. ;;; Permission is granted to any individual or institution to use, copy, modify,
  13. ;;; and distribute this software, provided that this complete copyright and
  14. ;;; permission notice is maintained, intact, in all copies and supporting
  15. ;;; documentation.
  16. ;;;
  17. ;;; Texas Instruments Incorporated provides this software "as is" without
  18. ;;; express or implied warranty.
  19. ;;;
  20.  
  21. ;;; Created 03/16/88 by LaMott G. OREN
  22.  
  23. (in-package 'xlib :use '(lisp))
  24.  
  25. (export 'using-gcontext)
  26.  
  27. #| TO DO:
  28.  
  29. Date: Wed, 22 Jun 88 14:51:22 -0700
  30. From: joel@wsl.dec.com
  31. Subject: gc cache 
  32. To:   lagoze@cu-arpa.cs.cornell.edu
  33. Cc:   joel@wsl.dec.com, xpert@athena.mit.edu, dec-toolkit@wsl.dec.com
  34. In-Reply-To: Your message of Wed, 22 Jun 88 14:26:27 -0700.
  35.              <8806222126.AA09675@gilroy.dec.com> 
  36.  
  37.  
  38. I improved the XtGetGC substantially recently, these changes will go into the
  39. R3 release.
  40.  
  41. The two biggest changes I made were to make the GC list self-organized: when
  42. you match against an existing GC, move it to the head of the list.  On the DEC
  43. mail program this reduced average search length from about 6.8 Matches per
  44. search down to 1.7 Matches per search.
  45.  
  46. Secondly, I vastly increased the speed of Match by checking against the most
  47. commonly specified fields first, then short-circuiting at two places if
  48. nothing else needed to be checked.  For the programs I was profiling, this got
  49. the two routines down into the noise.
  50.  
  51. There may still be a need to break up the linear list by hashing on, say,
  52. foreground, background, and font, maybe one or two others.  I'm always leery
  53. of linear lists.  But this is one place where locality of reference really
  54. minimized search length on the programs I profiled.
  55.  
  56. - Joel McCormack
  57.  
  58. |#
  59.  
  60. (defvar *gcontext-cache-length* 6 "No more than this many gcontexts in a cache")
  61.  
  62. ;; The default gcontext state defined by the X protocol.
  63. (defconstant *default-gcontext-state*
  64.          '#(3 #xffffffff 0 1 0 0 1 0 0 0 nil nil 0 0 nil 0 1 0 0 0 0 4 1 (0 0) (4) nil 1))
  65.  
  66. ;; Dummy gcontext containing the default gcontext state.
  67. ;; It may be (carefully) modified with the standard gcontext setf'ers
  68. ;; Avoid putting anything here containing a resource-id!
  69. (defparameter *default-gcontext*
  70.           (let ((default-state (copy-seq (the gcontext-state *default-gcontext-state*))))
  71.         (make-gcontext :local-state default-state :server-state default-state)))
  72.  
  73. ;; Default to NOT getting graphics exposure events
  74. (setf (gcontext-exposures *default-gcontext*) :off)
  75.  
  76. ;; Temp-Gcontext Resource
  77. (defvar *gcontext-cache* nil) ;; List of unused gcontexts
  78.  
  79. (defun allocate-gcontext ()
  80.   (or (atomic-pop *gcontext-cache*)
  81.       (make-gcontext)))
  82.  
  83. (defun deallocate-gcontext (gc)
  84.   (atomic-push gc *gcontext-cache*))
  85.  
  86. ;;;Note: Find-Gcontext puts the display in the gcontext structure.
  87. ;;;      However, when specifying a font, the gcontext-font setf'er
  88. ;;;      needs the display in the gcontext structure BEFORE
  89. ;;;      Find-Gcontext is called.  Because of this, when there's a font
  90. ;;;      parameter using-gcontext generates extra code to initialize the
  91. ;;;      display slot of the gcontext structure before the gcontext-font
  92. ;;;      setf'er is called.
  93.  
  94. (defmacro using-gcontext ((gcontext &rest options &key drawable default clip-ordering &allow-other-keys)
  95.               &body body)
  96.   ;; Equivalent to `(let ((gcontext (create-gcontext ,@options))) ,@body (free-gcontext ,gcontext))
  97.   ;; Except more efficient because gcontexts are cached.
  98.  
  99.   (declare-arglist
  100.     (gcontext &rest options &key default drawable
  101.           function plane-mask foreground background
  102.           line-width line-style cap-style join-style fill-style fill-rule
  103.           arc-mode tile stipple ts-x ts-y font subwindow-mode
  104.           exposures clip-x clip-y clip-mask clip-ordering
  105.           dash-offset dashes &allow-other-keys)
  106.            &body body)
  107.   (setq options (copy-list options))
  108.   (remf options :clip-ordering)
  109.   (remf options :default)
  110.   (remf options :drawable)
  111.  
  112.   (unless drawable (required-arg drawable))
  113.  
  114.   (let ((setfs nil)
  115.     (value (gensym))
  116.     (drawable-arg (and (getf options :font) (gensym))))
  117.     (do* ((option options (cddr option))
  118.       (name (car option) (car option)))
  119.      ((endp option) (setq setfs (nreverse setfs)))
  120.       ;; The CLX GContext stuff was engineered to make NIL values mean
  121.       ;; "unknown value".  Wrap SETF's with checks to ignore keyword
  122.       ;; options with NIL values.
  123.       (let ((val (cadr option))
  124.         (accessor `(,(xintern 'gcontext- name) ,gcontext ,@(when (eq name :clip-mask)
  125.                                `(,clip-ordering)))))
  126.     (cond ((constantp val)            ; Optimize for constant value
  127.            (when (eval val)
  128.          (push `(setf ,accessor ,val) setfs)))
  129.           ((atom val)            ; Optimize for variable value
  130.            (push `(when ,val (setf ,accessor ,val)) setfs))
  131.           (t (push `(let ((,value ,val))    ; Don't evaluate value expression twice
  132.               (when ,value (setf ,accessor ,value)))
  133.                setfs)))))
  134.     (when drawable-arg
  135.       (push `(setf (gcontext-display ,gcontext) (drawable-display ,drawable-arg))
  136.         setfs))
  137.  
  138.     (if (and default (null setfs))
  139.     `(let ((,gcontext ,default)) ,@body)
  140.       `(let ((,gcontext (allocate-gcontext))
  141.          ,@(when drawable-arg
  142.          (prog1 `((,drawable-arg ,drawable))
  143.             (setq drawable drawable-arg))))
  144.      (declare (type gcontext ,gcontext))
  145.      (replace (gcontext-local-state ,gcontext)
  146.           (gcontext-local-state ,(or default '(the gcontext *default-gcontext*))))
  147.      ,@setfs
  148.      (setq ,gcontext (find-gcontext ,gcontext ,drawable))
  149.      (multiple-value-prog1
  150.        (progn ,@body)
  151.        (return-gcontext ,gcontext))))))
  152.  
  153. ;;; Note: When find-gcontext can't find a match and must modify an
  154. ;;;       existing cache entry it ALWAYS modifies the oldest cache
  155. ;;;      entry.  I tried an algorithm that modified the "closest"
  156. ;;;      matching entry in the cache, and the cache thrashed 
  157. ;;;      terribly.  Perhaps modifying the closest matching entry 
  158. ;;;       in the oldest half of the cache?
  159.  
  160. (defun find-gcontext (gcontext drawable)
  161.   (let ((display (drawable-display drawable))
  162.     (local-state (gcontext-local-state gcontext))
  163.     (cache (getf (drawable-plist drawable) 'gcontext-cache)))
  164.     (declare (type list cache))
  165.     (declare-array gcontext-state local-state)
  166.     (with-display (display)            ; Get a lock, just in case
  167.       ;; The cache starts with a null cons.  This enables us to
  168.       ;; update the cache without searching the plist again.
  169.       (unless cache
  170.     (setf (getf (drawable-plist drawable) 'gcontext-cache)
  171.           (setq cache (list nil))))
  172.       ;; Ensure the timestamp and font match
  173.       (setf (gcontext-internal-timestamp local-state) 0)
  174.       (setf (gcontext-internal-font local-state) nil)
  175.       ;; Search for a compable gcontext
  176.       (do* ((previous cache entry)
  177.         (entry (cdr cache) (cdr entry))
  178.         (n 0 (index+ n 1)))
  179.        ((endp entry)
  180.         (if (< n *gcontext-cache-length*)
  181.         ;; Create a new gcontext if our limit isn't up
  182.         (create-temp-gcontext gcontext drawable)
  183.           ;; Else modify the last entry
  184.           (let* ((gc (car previous))
  185.              (new-state (gcontext-local-state gc)))
  186.         (declare (type gcontext gc))
  187.         (declare-array gcontext-state new-state)
  188.         ;; Modify GCONTEXT to reflect STATE. [force-gcontext-changes does the update]
  189.         (replace new-state local-state)
  190.         (setf (gcontext-internal-timestamp local-state) 0) ;; ensure changes are noticed
  191.         (rplacd (nthcdr n cache) nil) ;; delete GC from cache
  192.         (deallocate-gcontext gcontext)
  193.         gc)))
  194.     (declare (type list previous entry))
  195.     (let* ((gc (car entry))
  196.            (test-state (gcontext-local-state gc)))
  197.       (declare (type gcontext gc))
  198.       (declare-array gcontext-state test-state)
  199.       (when (equalp test-state local-state)    ; Found a match!
  200.         (setf (cdr previous) (cdr entry))
  201.         (deallocate-gcontext gcontext)
  202.         (return gc)))))))
  203.  
  204. (defun return-gcontext (gcontext)
  205.   (declare (type gcontext gcontext))
  206.   (let* ((drawable (gcontext-drawable gcontext))
  207.      (local-state (gcontext-local-state gcontext)))
  208.     (setf (gcontext-internal-timestamp local-state) 0)
  209.     (setf (gcontext-internal-font local-state) nil)
  210.     ;; most recently used gcontexts are at the front of the cache
  211.     (let ((cache (getf (drawable-plist drawable) 'gcontext-cache)))
  212.       (setf (cdr cache) (cons gcontext (cdr cache))))))
  213.  
  214.  
  215. (defun create-temp-gcontext (gcontext drawable)
  216.   ;; This is force-gcontext-changes with minor modifications
  217.   (declare (type gcontext gcontext))
  218.   (let* ((display (drawable-display drawable))
  219.      (server-state (gcontext-server-state gcontext))
  220.      (local-state (gcontext-local-state gcontext))
  221.      (gcontextid (allocate-resource-id display gcontext 'gcontext))
  222.      local-clip local-dash)
  223.     (declare-array gcontext-state server-state local-state)
  224.     (setf (gcontext-display gcontext) display
  225.       (gcontext-id gcontext) gcontextid
  226.       (gcontext-drawable gcontext) drawable)
  227.     (setf (gcontext-internal-timestamp local-state) 1)
  228.     (replace server-state (the gcontext-state *default-gcontext-state*))
  229.  
  230.     (with-display (display)
  231.       ;; first unpack a few things
  232.       (setq local-clip (gcontext-internal-clip local-state))
  233.       (if (setf (gcontext-internal-clip-mask local-state) (second local-clip))
  234.       (setf (gcontext-internal-clip server-state) local-clip)
  235.     (setf (gcontext-internal-clip-mask server-state) nil))
  236.       (setq local-dash (gcontext-internal-dash local-state))
  237.       (if (setf (gcontext-internal-dashes local-state) (first local-dash))
  238.       (setf (gcontext-internal-dash server-state) local-dash)
  239.     (setf (gcontext-internal-dashes server-state) nil))
  240.       (let ((local-font (gcontext-internal-font-obj local-state)))
  241.     (if local-font
  242.         (progn
  243.           (setf (gcontext-internal-font local-state) (font-id local-font))
  244.           (setf (gcontext-internal-font-obj server-state) local-font))
  245.       (setf (gcontext-internal-font server-state) nil)))
  246.       
  247.       ;; Because there is no locking on the local state we have to
  248.       ;; assume that state will change and set timestamps up front,
  249.       ;; otherwise by the time we figured out there were no changes
  250.       ;; and tried to store the server stamp as the local stamp, the
  251.       ;; local stamp might have since been modified.
  252.       (let ((new (the fixnum (gcontext-internal-timestamp server-state))))
  253.     (declare (type fixnum new))
  254.     (setq new (if (= new most-positive-fixnum)
  255.               1
  256.             (the fixnum (1+ new))))
  257.     (setf (gcontext-internal-timestamp server-state) new))
  258.       
  259.       (with-buffer-request (display *x-creategc*)
  260.     (resource-id gcontextid)
  261.     (drawable drawable)
  262.     (progn
  263.       (do ((i 0 (index+ i 1))
  264.            (bit 1 (the gcmask (ash bit 1)))
  265.            (nbyte 16)
  266.            (mask 0)
  267.            (local 0))
  268.           ((index>= i *gcontext-fast-change-length*)
  269.            (card32-put 12 mask)
  270.            (card16-put 2 (index-ash nbyte -2))
  271.            (index-incf (buffer-boffset display) nbyte))
  272.         (declare (type array-index i nbyte)
  273.              (type gcmask bit mask)
  274.              (type (or null card32) local))
  275.         (unless (eql (the (or null card32) (svref server-state i))
  276.              (setq local (the (or null card32) (svref local-state i))))
  277.           (setf (svref server-state i) local)
  278.           (card32-put nbyte local)
  279.           (setq mask (the gcmask (logior mask bit)))
  280.           (index-incf nbyte 4)))))
  281.       
  282.       ;; Update GContext extensions
  283.       (do ((extension *gcontext-extensions* (cdr extension))
  284.        (i *gcontext-data-length* (index+ i 1))
  285.        (local))
  286.       ((endp extension))
  287.     (unless (eql (svref server-state i)
  288.              (setq local (svref local-state i)))
  289.       (funcall (gcontext-extension-set-function (car extension))
  290.            gcontext local)
  291.       (setf (svref server-state i) local)))
  292.       
  293.       ;; Update clipping rectangles
  294.       (unless (eq (third local-clip)
  295.           (third (gcontext-internal-clip server-state)))
  296.     (setf (gcontext-internal-clip server-state) local-clip)
  297.     (with-buffer-request (display *x-setcliprectangles*)
  298.       (data (first local-clip))
  299.       (gcontext gcontext)
  300.       ;; XXX treat nil correctly
  301.       (card16 (or (gcontext-internal-clip-x local-state) 0)
  302.           (or (gcontext-internal-clip-y local-state) 0))
  303.       ;; XXX this has both int16 and card16 values
  304.       ((sequence :format int16) (third local-clip))))
  305.       
  306.       ;; Update dashes
  307.       (unless (eq (second local-dash)
  308.           (second (gcontext-internal-dash server-state)))
  309.     (setf (gcontext-internal-dash server-state) local-dash)
  310.     (with-buffer-request (display *x-setdashes*)
  311.       (gcontext gcontext)
  312.       ;; XXX treat nil correctly
  313.       (card16 (or (gcontext-internal-dash-offset local-state) 0)
  314.           (length (second local-dash)))
  315.       ((sequence :format card8) (second local-dash))))))
  316.   gcontext)
  317.